﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.ComponentModel;
using System.Windows.Forms;

namespace LowLevelGraphics.Types.Interval
{
    /// <summary>
    /// The boundaries are momentarily included (>= ...)
    /// If they should be excluded use ONLY greater or smaller sign
    /// </summary>
    [DebuggerDisplay("[{m_dMin}, {m_dMax}]")]
    [TypeConverter(typeof(IntervalConverter<double, DoubleInterval>))]
    public class DoubleInterval : IComparable<DoubleInterval>, ICloneable, ICustomTypeDescriptor
    {
        protected double m_dMin = 0.0f;
        protected double m_dMax = 0.0f;

        /// <summary>
        /// Default Constructor
        /// </summary>
        public DoubleInterval()
        {
        }

        /// <summary>
        /// constructor
        /// </summary>
        /// <param name="_dMin"></param>
        /// <param name="_dMax"></param>
        public DoubleInterval(double _dMin, double _dMax)
        {
            m_dMin = _dMin;
            m_dMax = _dMax;
        }

        /// <summary>
        /// Max value of this interval
        /// </summary>
        public double Max
        {
            get { return m_dMax; }
            set { m_dMax = value; }
        }

        /// <summary>
        /// Min value of this interval
        /// </summary>
        public double Min
        {
            get { return m_dMin; }
            set { m_dMin = value; }
        }

        /// <summary>
        /// Returns true, if the value is inside given interval
        /// </summary>
        /// <param name="_dValue"></param>
        /// <returns></returns>
        public bool Contains(double _dValue)
        {
            return _dValue >= m_dMin && _dValue <= m_dMax;
        }

        /// <summary>
        /// Check if the current interval completely contains given interval
        /// </summary>
        /// <param name="_doubleInterval"></param>
        /// <returns></returns>
        public bool Contains(DoubleInterval _doubleInterval)
        {
            return m_dMin<=_doubleInterval.m_dMin && m_dMax>= _doubleInterval.m_dMax;
        }

        /// <summary>
        /// This will check if an interval is overlapping, i.e. concrete:
        /// True if one of the given boundaries is contained inside the interval.
        /// The other boundary has to be outside the interval.
        /// </summary>
        /// <param name="_doubleInterval"></param>
        /// <returns></returns>
        public bool Intersects(DoubleInterval _doubleInterval)
        {
            return 
                (Contains(_doubleInterval.m_dMax) && !Contains(_doubleInterval.m_dMin))
                || (Contains(_doubleInterval.m_dMin) && !Contains(_doubleInterval.m_dMax))
                ;
        }

        /// <summary>
        /// This returns the size of the interval.
        /// Also negative sizes are allowed to show the direction.
        /// Use Abs(Size) if direction is unimportant
        /// </summary>
        public double Size
        {
            get { return m_dMax - m_dMin; }
        }

        #region IComparable<DoubleInterval> Members

        /// <summary>
        /// An interval is bigger if its size is bigger
        /// </summary>
        /// <param name="other"></param>
        /// <returns></returns>
        public int CompareTo(DoubleInterval other)
        {
            return this.Size.CompareTo(other.Size);
        }

        #endregion

        /// <summary>
        /// Get interval as string in notation e.g. [0,1]
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            return string.Format("[{0},{1}]", m_dMin, m_dMax);
        }

        #region ICloneable Members

        /// <summary>
        /// Use this to clone the interval
        /// </summary>
        /// <returns></returns>
        public object Clone()
        {
            return new DoubleInterval(this.m_dMin, this.m_dMax);
        }

        #endregion

        #region ICustomTypeDescriptor Members

        /// <summary>
        /// Returns a collection of custom attributes for this instance of a component.
        /// </summary>
        /// <returns>
        /// An <see cref="T:System.ComponentModel.AttributeCollection"/> containing the attributes for this object.
        /// </returns>
        public AttributeCollection GetAttributes()
        {
            return TypeDescriptor.GetAttributes(this, true);
        }

        /// <summary>
        /// Returns the class name of this instance of a component.
        /// </summary>
        /// <returns>
        /// The class name of the object, or null if the class does not have a name.
        /// </returns>
        public string GetClassName()
        {
            return TypeDescriptor.GetClassName(this, true);
        }

        /// <summary>
        /// Returns the name of this instance of a component.
        /// </summary>
        /// <returns>
        /// The name of the object, or null if the object does not have a name.
        /// </returns>
        public string GetComponentName()
        {
            return TypeDescriptor.GetComponentName(this, true);
        }

        /// <summary>
        /// Returns a type converter for this instance of a component.
        /// </summary>
        /// <returns>
        /// A <see cref="T:System.ComponentModel.TypeConverter"/> that is the converter for this object, or null if there is no <see cref="T:System.ComponentModel.TypeConverter"/> for this object.
        /// </returns>
        public TypeConverter GetConverter()
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Returns the default event for this instance of a component.
        /// </summary>
        /// <returns>
        /// An <see cref="T:System.ComponentModel.EventDescriptor"/> that represents the default event for this object, or null if this object does not have events.
        /// </returns>
        public EventDescriptor GetDefaultEvent()
        {
            return TypeDescriptor.GetDefaultEvent(this, true);
        }

        /// <summary>
        /// Returns the default property for this instance of a component.
        /// </summary>
        /// <returns>
        /// A <see cref="T:System.ComponentModel.PropertyDescriptor"/> that represents the default property for this object, or null if this object does not have properties.
        /// </returns>
        public PropertyDescriptor GetDefaultProperty()
        {
            return TypeDescriptor.GetDefaultProperty(this, true);
        }

        /// <summary>
        /// Returns an editor of the specified type for this instance of a component.
        /// </summary>
        /// <param name="editorBaseType">A <see cref="T:System.Type"/> that represents the editor for this object.</param>
        /// <returns>
        /// An <see cref="T:System.Object"/> of the specified type that is the editor for this object, or null if the editor cannot be found.
        /// </returns>
        public object GetEditor(Type editorBaseType)
        {
            return typeof(NumericUpDown);
        }

        /// <summary>
        /// Returns the events for this instance of a component using the specified attribute array as a filter.
        /// </summary>
        /// <param name="attributes">An array of type <see cref="T:System.Attribute"/> that is used as a filter.</param>
        /// <returns>
        /// An <see cref="T:System.ComponentModel.EventDescriptorCollection"/> that represents the filtered events for this component instance.
        /// </returns>
        public EventDescriptorCollection GetEvents(Attribute[] attributes)
        {
            return TypeDescriptor.GetEvents(this, attributes, true);
        }

        /// <summary>
        /// Returns the events for this instance of a component.
        /// </summary>
        /// <returns>
        /// An <see cref="T:System.ComponentModel.EventDescriptorCollection"/> that represents the events for this component instance.
        /// </returns>
        public EventDescriptorCollection GetEvents()
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Returns the properties for this instance of a component using the attribute array as a filter.
        /// </summary>
        /// <param name="attributes">An array of type <see cref="T:System.Attribute"/> that is used as a filter.</param>
        /// <returns>
        /// A <see cref="T:System.ComponentModel.PropertyDescriptorCollection"/> that represents the filtered properties for this component instance.
        /// </returns>
        public PropertyDescriptorCollection GetProperties(Attribute[] attributes)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Returns the properties for this instance of a component.
        /// </summary>
        /// <returns>
        /// A <see cref="T:System.ComponentModel.PropertyDescriptorCollection"/> that represents the properties for this component instance.
        /// </returns>
        public PropertyDescriptorCollection GetProperties()
        {
            return TypeDescriptor.GetProperties(this, true);
        }

        /// <summary>
        /// Returns an object that contains the property described by the specified property descriptor.
        /// </summary>
        /// <param name="pd">A <see cref="T:System.ComponentModel.PropertyDescriptor"/> that represents the property whose owner is to be found.</param>
        /// <returns>
        /// An <see cref="T:System.Object"/> that represents the owner of the specified property.
        /// </returns>
        public object GetPropertyOwner(PropertyDescriptor pd)
        {
            return this;
        }

        #endregion
    }
}
